<?php
namespace app\api\controller;

use think\Exception;
use think\facade\{Log, Db};

use app\BaseController;

use app\api\model\{UserShop, OrderCard};

class WxpayController extends BaseController
{
    protected $appid;
    protected $mch_id;
    protected $key;
    protected $openid;
    protected $out_trade_no;
    protected $body;
    protected $total_fee;
    protected $notify;
    
    public function __construct(){
        $system = getSystemVal();

        $this->appid  = $system['appid'];
        $this->mch_id = $system['mch_id'];
        $this->key    = $system['api_key'];
    }

    public function pay($data)
    {
        $this->openid       = $data['openid'];
        $this->out_trade_no = $data['order_no'];
        $this->total_fee    = $data['total_fee'];
        $this->body         = $data['body'];
        $this->notify       = $data['notify'];
        return $this->wxpay();
    }

    //微信小程序接口
    public function wxpay(){  //统一下单接口
        $unifiedorder = $this->unifiedorder();

        if($unifiedorder['return_code'] == 'SUCCESS' && $unifiedorder['result_code'] == 'SUCCESS'){//成功
            $parameters = array(
                'appId'     => $this->appid,
                'timeStamp' => '' . time() . '', //时间戳
                'nonceStr'  => $this->createNoncestr(), //随机串
                'package'   => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
                'signType'  => 'MD5'//签名方式
            );

            //签名
            $parameters['paySign'] = $this->getSign($parameters);

            $data = [
                'code' => 200,
                'sign' => $parameters,
            ];

            return $data;
        }else{
            $data = [
                'return_msg' => $unifiedorder['return_msg'],
                'code' => 400,
            ];

            if($unifiedorder['return_code'] == 'SUCCESS'){
                $data['return_msg'] = $unifiedorder['err_code_des'];
            }

            return $data;
        }
    }

    //统一下单接口
    private function unifiedorder()
    {
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

        $parameters = array(
            'appid'            => $this->appid, //小程序ID
            'mch_id'           => $this->mch_id, //商户号
            'nonce_str'        => $this->createNoncestr(), //随机字符串
            'body'             => $this->body,//商品描述
            'out_trade_no'     => $this->out_trade_no,//商户订单号
            // 'total_fee'        => floatval($this->total_fee * 100), //总金额 单位 分
            'total_fee'        => 1, //总金额 单位 分
            'spbill_create_ip' => $this->get_client_ip(), //终端IP
            'notify_url'       => config('app.url') . '/api/wxpay/' . $this->notify, //通知地址  确保外网能正常访问
            'openid'           => $this->openid, //用户id
            'trade_type'       => 'JSAPI',//交易类型
        );

        // 统一下单签名
        $parameters['sign'] = $this->getSign($parameters);
        $xmlData = $this->arrayToXml($parameters);
        // ff($xmlData);
        $return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));

        return $return;
    }

    // 支付回调
    public function notify()
    {
        //接收微信返回的数据数据,返回的xml格式
        $xmlData = file_get_contents('php://input');

        if(!$xmlData){
            return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
        }

        //将xml格式转换为数组
        $data = $this->xmlToArray($xmlData);

        //为了防止假数据，验证签名是否和返回的一样。
        if($this->checkSign($data, $this->key)){
            if($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS'){
                $out_trade_no = $data['out_trade_no'];  // 商户订单号

                $orderInfo  = Db::name('order')->where(['order_no' => $out_trade_no])->find();
                $userInfo   = Db::name('users')->where(['id' => $orderInfo['user_id']])->find();
                $goods_info = Db::name('goods')->where(['id' => $orderInfo['goods_id']])->find();

                $admin_id = $orderInfo['admin_id'];

                $admin_info = Db::name('admin')->where(['id' => $admin_id])->find();

                Db::startTrans(); 
                try {
                    /** 获得直购奖的ID */
                    $push_user_id = $orderInfo['to_user_id']?$orderInfo['to_user_id']:$userInfo['pid'];

                    if($push_user_id > 0){
                        /** 受益人在当前店铺的资产信息 */
                        $userPushShopInfo = Db::name('user_shop')->where(['user_id' => $push_user_id, 'admin_id' => $admin_id])->find();

                        if($userPushShopInfo && $userPushShopInfo['star_id'] > 0){
                            /** 查询受益人的直购奖比例 */
                            $push_money = Db::name('star')->where(['id' => $userPushShopInfo['star_id']])->value('bonus_ratio');

                            if($push_money > 0){
                                /** 增加受益人在当前店铺的佣金 */
                                $fst_money_flag = Db::name('user_shop')->where(['user_id' => $push_user_id, 'admin_id' => $admin_id])->inc('money', (int)$orderInfo['price'] * $push_money)->update();
                                
                                if($fst_money_flag === false){
                                    throw new Exception("更新会员佣金失败【" . $push_user_id . "】");
                                }
                                
                                /** 写入财务记录 */
                                $fst_fin_flag = addFin($push_user_id, $admin_id, $orderInfo['id'], 1, 1, '+' . (int)$orderInfo['price'] * $push_money, '消费佣金');

                                if($fst_fin_flag === false){
                                    throw new Exception("会员佣金记录添加失败【" . $push_user_id . "】");
                                }
                            }
                        }
                    }

                    /** 增加门店营收 */
                    Db::name('admin')->where(['id' => $admin_id])->inc('money', (int)$orderInfo['price'])->update();

                    /** 门店营收写入财务记录 */
                    $shop_fin_flag = addFin(0, $admin_id, $orderInfo['id'], 1, 1, '+' . $orderInfo['price'], '门店营收');

                    if($shop_fin_flag === false){
                        throw new Exception("门店营收记录添加失败【" . $orderInfo['id'] . "】");
                    }

                    /** 增加用户在当前店铺的总消费 */
                    $money_total_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id'], 'admin_id' => $admin_id])->inc('money_total', (int)$orderInfo['price'])->update();

                    if($money_total_flag === false){
                        throw new Exception("更新会员店铺总消费【" . $orderInfo['user_id'] . "】");
                    }

                    /** 增加用户在当前店铺的积分 */
                    $point_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id'], 'admin_id' => $admin_id])->inc('point', (int)$orderInfo['price'] * $admin_info['point_radio'])->update();

                    if($point_flag === false){
                        throw new Exception("更新会员在店铺的积分【" . $orderInfo['user_id'] . "】");
                    }

                    /** 写入财务记录 */
                    addFin($orderInfo['user_id'], $admin_id, $orderInfo['id'], 1, 2, '+' . (int)$orderInfo['price'] * $admin_info['point_radio'], '购买商品');

                    /** 更新用户在当前店铺的星级 */
                    $userShopModel = new UserShop();
                    $userShopModel->setStar($orderInfo['user_id'], $admin_id);

                    /** 订单状态更改 */
                    $order_change = Db::name('order')->where(['id' => $orderInfo['id']])->update(['status' => 2, 'pay_time' => time(), 'end_time' => time()]);

                    if($order_change === false){
                        throw new Exception("更新订单状态失败：单号【" . $orderInfo['order_no'] . "】");
                    }

                    /** 更新商品售卖数量 */
                    Db::name('goods')->where(['id' => $orderInfo['goods_id']])->inc('sell_number', $orderInfo['number'])->update();

                    /** 添加次卡 */
                    if($goods_info['type'] == 3){
                        $add_info = [
                            'order_id'        => $orderInfo['id'],
                            'user_id'         => $orderInfo['user_id'],
                            'admin_id'        => $orderInfo['admin_id'],
                            'goods_id'        => $orderInfo['goods_id'],
                            'card_number_all' => $goods_info['number'],
                            'card_number'     => $goods_info['number'],
                            'status'          => 1,
                            'create_time'     => time(),
                        ];

                        Db::name('user_card')->insert($add_info);
                    }

                    Db::commit();    
                }catch(Exception $e){
                    Db::rollback();
                    Log::write($e->getMessage());
                    return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
                }

                return $this->arrayToXml(['return_msg' => 'OK', 'return_code' => 'SUCCESS']);
            }
        }

        return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
    }

    // 团购支付回调
    public function notify_group()
    {
        //接收微信返回的数据数据,返回的xml格式
        $xmlData = file_get_contents('php://input');

        if(!$xmlData){
            return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
        }

        //将xml格式转换为数组
        $data = $this->xmlToArray($xmlData);

        //为了防止假数据，验证签名是否和返回的一样。
        if($this->checkSign($data, $this->key)){
            if($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS'){
                $out_trade_no = $data['out_trade_no'];  // 商户订单号

                $orderInfo  = Db::name('order_group')->where(['order_no' => $out_trade_no])->find();
                $userInfo   = Db::name('users')->where(['id' => $orderInfo['user_id']])->find();
                $group_info = Db::name('goods_group')->where(['id' => $orderInfo['group_id']])->find();
                $goods_info = Db::name('goods')->where(['id' => $group_info['goods_id']])->find();

                /** 店铺ID */
                $admin_id = $orderInfo['admin_id'];

                /** 查询店铺信息 */
                $admin_info = Db::name('admin')->where(['id' => $admin_id])->find();

                Db::startTrans(); 
                try {
                    /** 获得直购奖的ID */
                    $push_user_id = $orderInfo['to_user_id']?$orderInfo['to_user_id']:$userInfo['pid'];

                    if($push_user_id > 0){
                        /** 受益人在当前店铺的资产信息 */
                        $userPushShopInfo = Db::name('user_shop')->where(['user_id' => $push_user_id, 'admin_id' => $admin_id])->find();

                        if($userPushShopInfo && $userPushShopInfo['star_id'] > 0){
                            /** 查询受益人的直购奖比例 */
                            $push_money = Db::name('star')->where(['id' => $userPushShopInfo['star_id']])->value('bonus_ratio');
                            
                            if($push_money > 0){
                                /** 增加受益人在当前店铺的佣金 */
                                $fst_money_flag = Db::name('user_shop')->where(['user_id' => $push_user_id, 'admin_id' => $admin_id])->inc('money', (int)$orderInfo['price'] * $push_money)->update();
                                
                                if($fst_money_flag === false){
                                    throw new Exception("更新会员佣金失败【" . $push_user_id . "】");
                                }
                                
                                /** 写入财务记录 */
                                $fst_fin_flag = addFin($push_user_id, $admin_id, $orderInfo['id'], 5, 1, '+' . (int)$orderInfo['price'] * $push_money, '消费佣金');

                                if($fst_fin_flag === false){
                                    throw new Exception("会员佣金记录添加失败【" . $push_user_id . "】");
                                }
                            }
                        }
                    }

                    /** 增加门店营收 */
                    Db::name('admin')->where(['id' => $admin_id])->inc('money', (int)$orderInfo['price'])->update();

                    /** 门店营收写入财务记录 */
                    $shop_fin_flag = addFin(0, $admin_id, $orderInfo['id'], 5, 1, '+' . $orderInfo['price'], '门店营收');

                    if($shop_fin_flag === false){
                        throw new Exception("门店营收记录添加失败【" . $orderInfo['id'] . "】");
                    }

                    /** 增加用户在当前店铺的总消费 */
                    $money_total_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id'], 'admin_id' => $admin_id])->inc('money_total', (int)$orderInfo['price'])->update();

                    if($money_total_flag === false){
                        throw new Exception("更新会员店铺总消费【" . $orderInfo['user_id'] . "】");
                    }

                    /** 增加用户在当前店铺的积分 */
                    $point_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id'], 'admin_id' => $admin_id])->inc('point', (int)$orderInfo['price'] * $admin_info['point_radio'])->update();

                    if($point_flag === false){
                        throw new Exception("更新会员在店铺的积分【" . $orderInfo['user_id'] . "】");
                    }

                    /** 写入财务记录 */
                    addFin($orderInfo['user_id'], $admin_id, $orderInfo['id'], 5, 2, '+' . (int)$orderInfo['price'] * $admin_info['point_radio'], '购买商品');

                    /** 更新用户在当前店铺的星级 */
                    $userShopModel = new UserShop();
                    $userShopModel->setStar($orderInfo['user_id'], $admin_id);

                    /** 订单状态更改 */
                    $order_change = Db::name('order_group')->where(['id' => $orderInfo['id']])->update(['status' => 2, 'pay_time' => time()]);

                    if($order_change === false){
                        throw new Exception("更新订单状态失败：单号【" . $orderInfo['order_no'] . "】");
                    }

                    /** 重新获取订单信息 */
                    $order_info_new = Db::name('order_group')->where(['id' => $orderInfo['id']])->find();

                    if($order_info_new['chief_id'] != 0){
                        /** 查询团长订单信息 */
                        $chief_order = Db::name('order_group')->where(['user_id' => $order_info_new['chief_id'], 'status' => 2, 'admin_id' => $admin_id])->find();

                        /** 参团人订单ID */
                        $chief_ids = Db::name('order_group')->where(['chief_id' => $order_info_new['chief_id'], 'status' => 2, 'admin_id' => $admin_id])->column('id');

                        /** 拼团人数 - 团长 - 参团人数 == 0 表示拼团完成，修改参团订单状态 */
                        if($chief_order['chief_number'] - 1 - count($chief_ids) == 0){
                            $chief_ids[] = $chief_order['id'];

                            /** 拼团批量修改状态为已完成 */
                            Db::name('order_group')->where([['id', 'in', $chief_ids], ['admin_id', 'in', $admin_id]])->update(['status' => 3, 'end_time' => time()]);

                            /** 更新商品售卖数量 */
                            Db::name('goods')->where(['id' => $group_info['goods_id']])->inc('sell_number', $chief_order['chief_number'])->update();

                            if($goods_info['type'] == 3){
                                $chief_order_list = Db::name('order_group')->where([['id', 'in', $chief_ids]])->select();

                                $card_add_arr = [];
                                foreach($chief_order_list as $v){
                                    $add_info = [
                                        'order_id'        => $v['id'],
                                        'user_id'         => $v['user_id'],
                                        'admin_id'        => $admin_id,
                                        'goods_id'        => $goods_info['id'],
                                        'card_number_all' => $goods_info['number'],
                                        'card_number'     => $goods_info['number'],
                                        'order_type'      => 2,
                                        'status'          => 1,
                                        'create_time'     => time(),
                                    ];

                                    $card_add_arr[] = $add_info;
                                }

                                Db::name('user_card')->insertAll($card_add_arr);
                            }
                        }
                    }else if($order_info_new['chief_number'] == 1){
                        Db::name('order_group')->where(['id' => $orderInfo['id']])->update(['status' => 3, 'end_time' => time()]);

                        /** 更新商品售卖数量 */
                        Db::name('goods')->where(['id' => $goods_info['id']])->inc('sell_number', $orderInfo['number'])->update();

                        /** 添加次卡 */
                        if($goods_info['type'] == 3){
                            $add_info = [
                                'order_id'        => $orderInfo['id'],
                                'user_id'         => $orderInfo['user_id'],
                                'admin_id'        => $orderInfo['admin_id'],
                                'goods_id'        => $goods_info['id'],
                                'card_number_all' => $goods_info['number'],
                                'card_number'     => $goods_info['number'],
                                'order_type'      => 2,
                                'status'          => 1,
                                'create_time'     => time(),
                            ];

                            Db::name('user_card')->insert($add_info);
                        }
                    }

                    Db::commit();    
                }catch(Exception $e){
                    Db::rollback();
                    Log::write($e->getMessage());
                    return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
                }

                return $this->arrayToXml(['return_msg' => 'OK', 'return_code' => 'SUCCESS']);
            }
        }

        return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
    }

    // 充值支付回调
    public function notify_money()
    {
        //接收微信返回的数据数据,返回的xml格式
        $xmlData = file_get_contents('php://input');

        if(!$xmlData){
            return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
        }

        //将xml格式转换为数组
        $data = $this->xmlToArray($xmlData);

        //为了防止假数据，验证签名是否和返回的一样。
        if($this->checkSign($data, $this->key)){
            if($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS'){
                $out_trade_no = $data['out_trade_no'];  // 商户订单号

                /** 查询订单信息 */
                $orderInfo  = Db::name('order_money')->where(['order_no' => $out_trade_no])->find();

                /** 店铺ID */
                $admin_id = $orderInfo['admin_id'];

                /** 查询店铺信息 */
                $admin_info = Db::name('admin')->where(['id' => $admin_id])->find();

                Db::startTrans(); 
                try {
                    /** 增加门店营收 */
                    Db::name('admin')->where(['id' => $admin_id])->inc('money', (int)$orderInfo['number'])->update();

                    /** 门店营收写入财务记录 */
                    $shop_fin_flag = addFin(0, $admin_id, $orderInfo['id'], 8, 3, '+' . $orderInfo['number'], '门店营收');

                    if($shop_fin_flag === false){
                        throw new Exception("门店营收记录添加失败【" . $orderInfo['id'] . "】");
                    }

                    /** 增加在当前店铺的余额 */
                    $user_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id']])->inc('card_money', $orderInfo['number'])->update();
                            
                    if($user_flag === false){
                        throw new Exception("更新会员充值余额失败【" . $orderInfo['user_id'] . "】");
                    }

                    /** 写入财务记录 */
                    addFin($orderInfo['user_id'], $admin_id, $orderInfo['id'], 8, 3, '+' . $orderInfo['number'], '充值');

                    /** 增加用户在当前店铺的积分 */
                    $point_flag = Db::name('user_shop')->where(['user_id' => $orderInfo['user_id'], 'admin_id' => $admin_id])->inc('point', (int)$orderInfo['number'] * $admin_info['point_radio'])->update();

                    if($point_flag === false){
                        throw new Exception("更新会员在店铺的积分【" . $orderInfo['user_id'] . "】");
                    }

                    /** 写入财务记录 */
                    addFin($orderInfo['user_id'], $admin_id, $orderInfo['id'], 8, 2, '+' . $orderInfo['number'] * $admin_info['point_radio'], '充值');

                    /** 订单状态更改 */
                    $order_change = Db::name('order_money')->where(['id' => $orderInfo['id']])->update(['status' => 2, 'pay_time' => time(), 'end_time' => time()]);

                    if($order_change === false){
                        throw new Exception("更新订单状态失败：单号【" . $orderInfo['order_no'] . "】");
                    }

                    Db::commit();    
                }catch(Exception $e){
                    Db::rollback();
                    Log::write($e->getMessage());
                    return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
                }

                return $this->arrayToXml(['return_msg' => 'OK', 'return_code' => 'SUCCESS']);
            }
        }

        return $this->arrayToXml(['return_msg' => 'ERROR', 'return_code' => 'FAIL']);
    }

    private function getSigns($arr,$key)
    {
        ksort($arr);
        foreach ($arr as $k => $v) {
            if (empty($v)) {
                unset($arr[$k]);
            }
        }

        ksort($arr);

        $stringA = $this->arrToStr($arr);
        return strtoupper(md5($stringA . "&key=".$key));
    }

    //作用：产生随机字符串，不长于32位
    private function createNoncestr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    private function arrToStr($arr)
    {
        ksort($arr);
        $str = '';
        foreach ($arr as $k => $v) {
            $str .= $k . "=" . $v . "&";
        }

        return rtrim($str,"&");
    }

    private static function postXmlCurl($xml, $url, $second = 30)
    {
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 40);
        set_time_limit(0);

        //运行curl
        $data = curl_exec($ch);

        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            echo "curl出错，错误码:$error" . "<br>";
            // throw new WxPayException("curl出错，错误码:$error");
        }
    }

    //数组转换成xml
    private function arrayToXml($arr)
    {
        $xml = "<root>";
        foreach ($arr as $key => $val) {
            if (is_array($val)) {
                $xml .= "<" . $key . ">" . $this->arrayToXml($val) . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            }
        }

        $xml .= "</root>";
        return $xml;
    }

    private function checkSign($data,$key)
    {
        $sign_return = $data['sign'];
        unset($data['sign']);
        $sign = $this->getSigns($data,$key);

        if ($sign_return != $sign) {
            return false;
        }

        return true;
    }

    //xml转换成数组
    private function xmlToArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);

        return $val;
    }

    /** 获取当前服务器的IP */
    private function get_client_ip()
    {
        if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
        } else {
            $cip = "unknown";
        }
        return $cip;
    }

    //作用：生成签名
    private function getSign($Obj)
    {
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //签名步骤二：在string后加入KEY
        $String = $String . "&key=" . $this->key;

        //签名步骤三：MD5加密
        $String = md5($String);
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        return $result_;
    }

    ///作用：格式化参数，签名过程需要使用
    private function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);

        foreach($paraMap as $k => $v){
            if($urlencode){
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }

        $reqPar = '';

        if(strlen($buff) > 0){
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }

        return $reqPar;
    }
}